When writing code during the development of software applications, developers commonly spend a significant amount of time “debugging” the code to find runtime and other source code errors. In doing so, developers may take several approaches to reproduce and localize a source code bug, such as observing behavior of a program based on different inputs, inserting debugging code (e.g., to print variable values, to track branches of execution, etc.), temporarily removing code portions, etc. Tracking down runtime errors to pinpoint code bugs can occupy a significant portion of application development time.
Many types of debugging applications (“debuggers”) have been developed in order to assist developers with the code debugging process. These tools offer developers the ability to trace, visualize, and alter the execution of computer code. For example, debuggers may visualize the execution of code instructions, may present code variable values at various times during code execution, may enable developers to alter code execution paths, and/or may enable developers to set “breakpoints” and/or “watchpoints” on code elements of interest (which, when reached during execution, causes execution of the code to be suspended), among other things.
An emerging form of debugging applications enable “time travel,” “reverse,” or “historic” debugging. With “time travel” debugging, execution of a program (e.g., executable entities such as threads) is recorded/traced by a trace application into one or more trace files. These trace file(s) can then be used to replay execution of the program later, for both forward and backward analysis. For example, “time travel” debuggers can enable a developer to set forward breakpoints/watchpoints (like conventional debuggers) as well as reverse breakpoints/watchpoints.
In order to facilitate recording, existing “time travel” debuggers force applications that are being recorded to execute as if they were single-threaded and executing at one single-threaded processor core, regardless of whether or not the application is capable of being executed multi-threaded. This done so that the existing debuggers are able to record a complete record of execution of the application, including recording a full ordering of everything that happens during execution of the application. However, this introduces severe overheads during trace recording and trace replay (i.e., processing, memory, disk space usage overheads), leads to exceptionally large trace files, and prevents recording in scenarios in which multiple processes need to be recorded—such as is the case with distributed applications.